#
# This file is importedd by the XML template, for instrument specific handling
#
# Hopefully OxfordDatasetBuilder is a bit mor ecoherent than BrukerFrameBuilder
#

from config import DataFormats
from datasetBuilder import DatasetBuilder


class OxfordDatasetBuilder(DatasetBuilder):
    """ The idea here is to make the garbage we extract from 
the Bruker ApexII PostgreSQL database as pallatable to the 
XML template parser as possible. """

    def __init__(self,dataFormat=DataFormats.NOVA):
      super(OxfordDatasetBuilder,self).__init__(dataFormat)
#      self.dataFormat = dataFormat
      self.protonums = []



    def buildDatasetsFromAggregatedFiles(self, filesets, priordata=None, purgelist=None):
      """This method builds a datastructure that the XML template method can
      iterate over to dump whole datasets of files and parameters.

      Made an executive decision to treat each scan as a dataset 
      For each image, there should be a corresponding  \work\image.raw
      integrated intensity file. should we archive those derived files too?
      """
      self.priordata = priordata # local ref
      self.purgelist = purgelist # local ref
      # from ntpath import split # for windows
      from os.path import split # for windows
      import os.path 
      import re, types
      dsetnum_re = re.compile(r".*\D(\d+)$")  # trailing digits from 
      if isinstance(filesets, list):
         # uh oh
         print "No frame file data"
         return None
      keys = filesets.keys()
      keys.sort()
      dsets = []

      # in principle, filesets are aggregated lists of recently 
      # modified or created files - i.e. files that have changed. 
      # priordata, on the otherhand, has already been injected 
      # into ICAT. reinjecting without prior removal is not allowed
      print "priordata", priordata 
      
      for key in keys:  # loop over file groups
        # for key = SUNDRIES and INDEXING, the split doesn't work
        # in particular,there will be no dsetnum
        dir, dsetname = split(key) # ntpath split
        filelist = filesets[key] 
        filelist.sort() # ensure ordered filelist
        dsetnum = dsetnum_re.match(key)
        if not dsetnum:
          # handle this dataset some other way???
          print  key
          if key == DataFormats.INDEXING: 
             scan = "indexing"
          else:
             scan = "unknown"
          dset = {'proto':None, 'num':None, 'mach':None, 
                'dsetname':dsetname,
                'description': "additional files",
                'params':[] , 'scan':scan }
          files = []
          for file in filelist:
            dir , filename = split(file)
            print dir, filename
            version = "?"
            elements = [ #{'name': 'description',
                         # 'content': '' }, 
#                         {'name': 'datafile_version',
#                          'content': '1' }, 
#                         {'name': 'datafile_version_comment',
#                          'content': '' }, 
                         {'name': 'datafile_format',
                          'content': 'unknown' }, 
                         {'name': 'datafile_format_version',
                          'content': version }, 
              ]
            self.addCommonFileData(file, elements)
            file = {'dir':dir, 'name':filename, 'params': [ ],
               'elements': elements }
            # probably we need to specify file types etc 
            # and or remove ICAT unsupported/registered format types
            self.purgeCheck(file)
            files.append(file)
          dset['files'] = files
          dsets.append(dset)
             
          continue

        dsetnum = dsetnum.group(1)

        sid = int(dsetnum)  # convert string to number
        if sid <1 or sid > len(self.operations): 
          """ So iether the Apex bruker software went wrong, OR,
              more likely, this is older SMART data.
          """
          # handle this dataset some other way???
          # just as a group of files ??? TBD...
#####          print "No Bruker Postgres Scan operation info for ", key
          print "Building metadata for scan",key
          # smart has fixed chi, so only omega scan is possible, mainly.
          dset = {'proto':None, 'num':None, 'mach':None, 
                'dsetname':dsetname,
                'description': "Scan frame data",
                'params':[] , 'scan':"omega scan" }#assumed. no idea what kind
          files = []
          version = None
          file0 = filelist[0]
          file1 = filelist[-1]
          meta0 = None; meta1 = None
          try:
                from fileHandlers.oxfordProcess import OXFORD
                filepath = dir + "/" + file0
                f = open(filepath)
                sf = OXFORD(f)
                meta0 =  sf.getMetadata()
                f.close()
                filepath = dir + "/" + file1
                f = open(filepath)
                sf = OXFORD(f)
                meta1 =  sf.getMetadata()
                f.close()
          except Exception, e:
                print e
          if file0 and file1 and file0!= file1 and meta0 and meta1:
             version =  meta0['version']
             width    = float(meta0['oscEnd'])- float(meta0['oscStart'])
             delta    = width            #  float(meta0['INCREME'])
             axis     = int(meta0['scanaxis']) 
             axisname = meta0['oscAxis']
             distance = float(meta0['distance'])
             wavelen  = float(meta0['wavelength'])
             time     = float(meta1['exposureTime'])
             startang = meta0['startangles'][1:5]
             endang   = meta1['endangles'][1:5]
             frames   = len(filelist)   #  meta1['NUMBER']
             range    = float(endang[axis-1]) - float(startang[axis-1])
             speed    = delta /time
             types = {1:"Theta scan",2:"Omega scan",3:"Chi scan", 4:"Phi scan",9:"Still scan",
                 12:"Phi360 scan",14:"Theta/2Theta scan", 15:"Chi scan" }
             params = [ 
          {'name': '_diffrn_measurement.method',
                        'sval': axisname + " scan",
                        'units': 'n/a'},
          {'name': '_diffrn_measurement.sample_detector_distance',
                        'nval': distance,
                        'units': 'mm'},
          {'name': '_diffrn_scan.integration_time',
                        'nval': time, 
                        'units': 's'},
          {'name': '_diffrn_scan.SPEED',
                        'nval': speed,
                        'units': u'\xb0/s'},
          {'name': '_diffrn_scan.id',
                        'sval': sid,
                        'units': 'n/a'},
#          {'name': '_diffrn_scan.frames',
#                        'nval': self.protonums[n],
#                        'units': 'n/a'},
          {'name': '_diffrn_scan.AXIS_ANGLE_RANGE',
                        'nval': range,
                        'units': u'\xb0'},
          {'name': '_diffrn_scan.AXIS_ANGLE_DIRECTION',
                        'nval':  -1 if delta <0.0 else +1 , 
                        'units': 'n/a'},
          {'name': '_diffrn_scan.AXIS_ANGLE_INCREMENT',
                        'nval':  delta,
                        'units': u'\xb0'},
          {'name': '_diffrn_scan.DETECTOR_AXIS_ANGLE_TWOTHETA',
                        'nval': startang[0],
                        'units': u'\xb0'},
          {'name': '_diffrn_scan.GONIOMETER_AXIS_ANGLE_OMEGA',
                        'nval': startang[1],
                        'units': u'\xb0'},
          {'name': '_diffrn_scan.GONIOMETER_AXIS_ANGLE_CHI',
                        'nval': startang[2],
                        'units': u'\xb0'},
          {'name': '_diffrn_scan.GONIOMETER_AXIS_ANGLE_PHI',
                        'nval': startang[3],
                        'units': u'\xb0'},
             ]
          else:
             version = "?"
             params = { }
          

          for file in filelist:
            dir1, filename = split(file)
            filepath = dir + "/" + file
            """
            if not version:  # get version number only for first data file
              try:
                from sfrmProcess import SFRM
                f = open(filepath)
                sf = SFRM(f)
                meta =  sf.getMetadata()
                f.close()
                version =  meta['VERSION']
              except Exception, e:
                print e
                version = "?"
            """
            elements = [ 
#                         {'name': 'datafile_version_comment',
#                          'content': '' }, 
                         {'name': 'datafile_format',
                          'content': 'oxford img' }, 
                         {'name': 'datafile_format_version',
                          'content': version }, 
              ]
            self.addCommonFileData(filepath,elements)
            file = {'dir':dir, 'name':filename, 'params': [ ],
               'elements': elements }
            # probably we need to specify file types etc 
            # and or remove ICAT unsupported/registered format types
            self.purgeCheck(file)
            files.append(file)
          dset['params'].append( {'name': '_diffrn_scan.frames',
                     'nval': len(filelist),
                     'units': 'n/a'} )
          dset['params'].extend(params)
          dset['files'] = files
          dsets.append(dset)
          continue
         
        ops = self.operations[sid-1]

#        set = protolist[i]
#        if not set: continue  # rationalised from existance
#        num = self.protonums[i]
        num = len(filelist)
        set = "ppppp"
        #if set[-1]=='sfrm':
        if filelist[0].endswith('.sfrm'):
          type = 'Apex'
        else:
          type = 'SMART'

        scantype = ops[0]['sval']
      
        ops.append( {'name': '_diffrn_scan.frames',
                     'nval': len(filelist),
                     'units': 'n/a'} )
        siter = sid
        dset = {'proto':set, 'num':len(filelist), 'mach':type, 
                'dsetname':dsetname,
                'description': "Scan frame data",
                'params':ops, 'scan':scantype }
        
        print dsetname
        files = []
        version = None
        for file in filelist:
          filepath = dir + "/" + file
          filename = file
          # dir,filename = split(file)
          if type == 'Apex':
             i = int(''.join(filename[-9:-5]))
          else:
             i = int(''.join(filename[-3:]))

          if not version:  # get version number only for first data file
            try:
              from fileHandlers.sfrmProcess import SFRM
              f = open(filepath)
              sf = SFRM(f)
              meta =  sf.getMetadata()
              f.close()
              version =  meta['VERSION']
            except Exception, e:
              print e
              version = "?"
          elements = [ {'name': 'description',
                          'content': 'frame' }, 
                         {'name': 'datafile_version',
                          'content': '1' }, 
                         {'name': 'datafile_version_comment',
                          'content': '' }, 
                         {'name': 'datafile_format',
                          'content': 'bruker sfrm' }, 
                         {'name': 'datafile_format_version',
                          'content': version }, 
              ]
          self.addCommonFileData(filepath,elements)

          file = {'dir':dir, 'name':filename, 
            'elements': elements,
            'params': [ 
              {'name': '_diffrn_scan_frame.frame_id',
                        'sval': filename, 
                        'units': 'n/a'},
              {'name': '_diffrn_scan_frame.scan_id',
                        'nval': sid,
                        'units': 'n/a'},
              {'name': '_diffrn_scan_frame.frame_number',
                        'nval': i, 
                        'units': 'n/a'},
            ],
          }

          # Maybe should add  extra params here, scan, frame id, angles 
#_diffrn_scan_frame.GONIOMETER_OMEGA  # at start of scan step
#_diffrn_scan_frame.GONIOMETER_KAPPA  # at start of scan step kappa/phi
#_diffrn_scan_frame.GONIOMETER_CHI    # at start of scan or chi/phi'
#_diffrn_scan_frame.GONIOMETER_PHI    # at start of scan step
#_diffrn_scan_frame.GONIOMETER_PHIP   # at start of scan step   phi'
#_diffrn_scan_frame.DETECTOR_TWO_THETA   # at start of scan step
#_diffrn_scan_frame.DETECTOR_DISTANCE # at start of scan 
#_diffrn_scan_frame.SCAN_TYPE         # omega or phi i.e what angle changed?
#     #  don't know about omega/2theta scans. I'm not sure if they are used?
#_diffrn_scan_frame.SCAN_END           # end angle after scan.
#                             # maybe it should just be the +/- incr/decr 
#                             # because there are two ways to get there?

          self.purgeCheck(file)
          files.append(file)
        dset['files'] = files
        dset['_diffrn_scan.frame_id_start'] = files[0]['name'] 
        dset['_diffrn_scan.frame_id_end']  = files[-1]['name']
        dsets.append(dset)

      return dsets


    def buildDatasets(self, protolist):
      """Made an executive decision to treat each scan as a dataset 
      For each image, there should be a corresponding  \work\image.raw
      integrated intensity file. should we archive those derived files too?
      """
      protolist = self.rationaliseDatasetData(protolist)

      pnum = len(self.protonums)  # num of each scan
      if pnum != len(protolist):  # file protos for each scan
        raise Exception, "mismatch between SAINT framename and integrate_nframes " + fileproto

      dsets = []
      

      for i in range(0,pnum):
        set = protolist[i]
        if not set: continue  # rationalised from existance
        num = self.protonums[i]
        if set[-1]=='sfrm':
          type = 'Apex'
          sfld = -3
        else:
          type = 'SMART'
          sfld = -1
        sid = int(set[sfld-2])
        print sid, len(self.operations)
        if sid>0 and sid<=len(self.operations): # apparently the database is screwed up sometimes.
          ops = self.operations[sid-1]
          scantype = ops[0]['sval']
        else:
          scantype = 'unknown'
          ops = []

        ops.append( {'name': '_diffrn_scan.frames',
                     'nval': num,
                     'units': 'n/a'} )
        siter = set[sfld]
        iter = int(siter)
        lit = len(siter)
        dset = {'proto':set, 'num':num, 'mach':type, 
                'dsetname':"".join(set[1:sfld-1]),
                'params':ops, 'scan':scantype }
        
        #for key in ops.keys():
        #  dset[key] = ops[key]  # copy 
        files = []
        format = "%%0%dd" % lit
        for idx in range(iter,iter+num):
          dir = set[0] 
          sequ = format % idx
          new =  set[1:]
          new[sfld] = sequ
          filename =''.join(new)
          file = {'dir':dir, 'name':filename, 'params': 
            [ {'name': '_diffrn_scan_frame.frame_id',
                        'sval': filename, 
                        'units': 'n/a'},
              {'name': '_diffrn_scan_frame.scan_id',
                        'nval': i, 
                        'units': 'n/a'},
              {'name': '_diffrn_scan_frame.frame_number',
                        'nval': idx, 
                        'units': 'n/a'},
            ]
          }

          # Maybe should add  extra params here, scan, frame id, angles 
#_diffrn_scan_frame.GONIOMETER_OMEGA  # at start of scan step
#_diffrn_scan_frame.GONIOMETER_KAPPA  # at start of scan step kappa/phi
#_diffrn_scan_frame.GONIOMETER_CHI    # at start of scan or chi/phi'
#_diffrn_scan_frame.GONIOMETER_PHI    # at start of scan step
#_diffrn_scan_frame.GONIOMETER_PHIP   # at start of scan step   phi'
#_diffrn_scan_frame.DETECTOR_TWO_THETA   # at start of scan step
#_diffrn_scan_frame.DETECTOR_DISTANCE # at start of scan 
#_diffrn_scan_frame.SCAN_TYPE         # omega or phi i.e what angle changed?
#     #  don't know about omega/2theta scans. I'm not sure if they are used?
#_diffrn_scan_frame.SCAN_END           # end angle after scan.
#                             # maybe it should just be the +/- incr/decr 
#                             # because there are two ways to get there?

          files.append(file)
        dset['files'] = files
        dset['_diffrn_scan.frame_id_start'] = files[0]['name'] 
        dset['_diffrn_scan.frame_id_end']  = files[-1]['name']
        dsets.append(dset)
              
      return dsets


if __name__ == '__main__':
    #import and possibly override, default config info
    from mapConfig import *


    bfb = OxfordDatasetBuilder()
       
    for sample in frames:
       sid = sample[0]
       if sid != 3: continue
       rev = sample[1]
       protonums = sample[2]
       fileproto = sample[3]
       fields = "operation_types_id, time, dx, sweep, direction, width, theta, omega, chi, phi, speed"
       try:
         print sid, fileproto
         #ops = getOperations(dbcnx,sid,rev)
         ops = getStrategyScans(dbcnx,sid,rev)
         print fields
         for op in ops:
           print op
         zzz = bfb.deduceFrames(fileproto,protonums, ops)
         if zzz:
           for it in zzz:
             print "   ", it
           yyy = bfb.buildDatasets(zzz)
           #print yyy
           print protonums
         else: 
           print None 

       except Exception, m:
         import traceback
         traceback.print_exc(file=sys.stdout)
         print sid, m
         
      
